"""
完整脚本：从 Excel 提取并生成 3 个案例的输出
- 输入文件（默认）：./data/data.xlsx
- 输出：
- 输出文件路径：./out/analysis_outputs_v1.0
    case2_maturity.xlsx     （赛道聚合与成熟度得分）

- 目标：
    1. 赛道成速度：使用 熵权法 + TOPSIS 得出成熟度评分 0 ~ 100
"""

import os
import numpy as np
import pandas as pd
import utils.excel_util as excel_util

# --------------------- 配置区 ---------------------
INPUT_XLSX = "./data/data.xlsx"
OUT_SHEET_NAME = "赛道成熟度 - 熵权法 + TOPSIS"  # 如果表名不同请修改
OUTPUT_DIR = "./out/analysis_outputs_v2.0"
READ_SHEET_NAME = ["全国=21-25", "上海=21-25"]


# --------------------- 辅助函数 ---------------------

def min_max_norm(series):
    mi = series.min()
    ma = series.max()
    if ma - mi < 1e-12:
        return np.zeros(len(series))
    return (series - mi) / (ma - mi)


def deal_stage(series):
    """
    :param series:要处理临床阶段数据
    临床实验阶段处理：
        1. 将所有阶段合并为 1-4 级
        2. 包含 x 期的一律按照 x 期处理，（x= 1，2，3，4）权重依次递减
        3. 基础科学研究 or 探索性研究/预实验 or 卫生服务研究 or 诊断实验新技术临床研究 or 治疗新技术临床实验 = 1
        4. 其他 or 回顾性研究 = 4
        5. Ⅰ = 1 and Ⅱ = 2 and Ⅲ = 3 and Ⅳ = 4
        注释：移除 其他，数量太多
        调整分数：Ⅰ = 0.1 and Ⅱ = 0.6 and Ⅲ = 1 and Ⅳ = 1.5
    :return:求和
    """
    conditions = [
        # 1分
        series.str.startswith("1期") |
        series.str.contains("基础科学研究") |
        series.str.contains("探索性研究") |
        series.str.contains("预实验") |
        series.str.contains("卫生服务研究") |
        series.str.contains("诊断实验新技术临床研究") |
        series.str.contains("治疗新技术临床实验"),
        # 2分
        series.str.startswith("2期"),
        # 3分
        series.str.startswith("3期"),
        # 4分
        series.str.startswith("4期") |
        series.str.contains("其他") |
        series.str.contains("回顾性研究")
    ]

    choices = [1, 2, 3, 4]

    return np.select(conditions, choices, default=0).sum()


# ---------------- 对数 + 百分位缩放函数 ----------------
def log_percentile_scale(df, cols, base=np.e, lower=1, upper=99):
    """
    对指定列做对数 + 百分位缩放，保持在 lower-upper 之间
    参数：
        df: DataFrame
        cols: 需要处理的列名列表
        base: 对数底数
        lower: 缩放最小值
        upper: 缩放最大值
    """
    df_scaled = df.copy()
    for col in cols:
        # 对数变换（加1避免0）
        log_col = np.log1p(df[col].astype(float)) / np.log(base)
        # 取 1%–99% 百分位，避免极值拉满
        p_low, p_high = np.percentile(log_col, [1, 99])
        clipped = np.clip(log_col, p_low, p_high)
        # 线性映射到 lower-upper
        scaled = (clipped - clipped.min()) / (clipped.max() - clipped.min() + 1e-12) * (upper - lower) + lower
        df_scaled[col] = scaled
    return df_scaled


# ---------------- 熵权 + TOPSIS + 对数百分位缩放 ----------------
def deal_data_log_percentile(
        original_data,
        need_deal_data,
        log_base=np.e,
        is_log_scale=False,
        expansion_factor=1.0):
    """
    使用熵权法 + TOPSIS 计算成熟度得分，并对指标 + 成熟度得分做 log + 百分位缩放
    参数：
        original_data: DataFrame，原始指标数据
        need_deal_data: 指标列名列表
        log_base: 对数底数
        is_log_scale: 是否对指标 + 成熟度得分做对数百分位缩放
        expansion_factor: 成熟度得分扩展因子，默认为1.0
    返回：
        DataFrame，指标 + 成熟度得分都在1-99
    """
    X = original_data[need_deal_data].astype(float).values

    # 1️⃣ 数据标准化（min-max，用于熵权 + TOPSIS）
    X_norm = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0) + 1e-12)

    # 2️⃣ 熵权法计算权重
    P = X_norm / (X_norm.sum(axis=0, keepdims=True) + 1e-12)
    k = 1.0 / np.log(X_norm.shape[0])
    E = -k * (P * np.log(P + 1e-12)).sum(axis=0)
    w = (1 - E) / (1 - E).sum()
    weights_dict = dict(zip(need_deal_data, w))
    # for indicator, weight in weights_dict.items():
    # print(f"指标 '{indicator}' 的权重: {weight:.4f}")

    # 3️⃣ TOPSIS
    V_pos = X_norm.max(axis=0)  # 正理想解
    V_neg = X_norm.min(axis=0)  # 负理想解
    D_pos = np.sqrt(((X_norm - V_pos) ** 2 * w).sum(axis=1))
    D_neg = np.sqrt(((X_norm - V_neg) ** 2 * w).sum(axis=1))
    topsis_score = D_neg / (D_pos + D_neg)

    # 5️⃣ 成熟度原始分数
    original_data["成熟度得分（原始）"] = topsis_score

    # 4️⃣ 将成熟度得分先映射到 1-99
    original_data["成熟度得分（映射）"] = ((topsis_score - topsis_score.min()) / (
            topsis_score.max() - topsis_score.min() + 1e-12) * 98 + 1) * expansion_factor  # 1–99

    # 5️⃣ 对指标 + 成熟度得分做 log + 百分位缩放
    if is_log_scale:
        original_data["成熟度得分（缩放）"] = topsis_score
        result_data = log_percentile_scale(original_data, ["成熟度得分（缩放）"], base=log_base, lower=1, upper=99)
        return result_data.sort_values("成熟度得分（原始）", ascending=False)

    return original_data.sort_values("成熟度得分（原始）", ascending=False)


def thermodynamic_diagram(original_data, title, year, score_type):
    """
    :original_data:  目标数据数据
    :t:             赛道或靶点
    :y:             年份
    :score_type:    分数类型
    :return:
    """
    # original_data = original_data[original_data["成熟度得分"] > 25]
    # 首先，确保“成熟度得分”列存在且为数值类型
    original_data[score_type] = pd.to_numeric(original_data[score_type], errors='coerce')
    # 根据成熟度得分排序，从高到低
    sorted_data = original_data.sort_values(by=score_type, ascending=False)
    # 取排序后的前十行
    top_data = sorted_data.head(10)
    value_columns = ["实验数", "企业数", "上市数", "临床阶段"] + [score_type]
    long_df = top_data.melt(
        id_vars=[title],
        value_vars=value_columns,
        var_name="特征值",
        value_name="值"
    )

    # 重新排序列顺序
    long_df = long_df[[title, "特征值", "值"]]

    return long_df


# --------------------- 主流程 ---------------------
def main(input_xlsx=INPUT_XLSX, sheet_name="Sheet1", output_dir=OUTPUT_DIR):
    excel_all_data = {}
    for e in READ_SHEET_NAME:
        df = excel_util.input_excel(input_xlsx, e)
        # 规范列名（去两端空格）
        df.columns = [c.strip() for c in df.columns]
        excel_all_data[e] = df

    # ----------------- 案例二：赛道成熟度 -----------------
    print("开始：案例二（赛道成熟度）处理...")
    detail_result = {}
    thermodynamic_diagram_result = {}
    thermodynamic_diagram_with_year = {}
    # 全国 和 上海
    for data_key, data_value in excel_all_data.items():

        case2_cols = ["年份", "临床试验编号", "所属分期", "所属靶点", "赛道", "上市情况", "企业名称"]
        case2_exist = [c for c in case2_cols if c in data_value.columns]
        case2 = data_value[case2_exist].copy()

        # 聚合：按赛道，年份 分组：试验数总和、企业数（对应不同药物，不需要去重）、上市企业数、分期
        # 两种分组：赛道和靶点
        detail_type = ["所属靶点", "赛道"]
        detail_year = [2025, 2024, 2023, 2022, 2021]
        for t in detail_type:
            for y in detail_year:
                case2_extract = case2[case2["年份"] == y]
                # 开始分组计算
                group = case2_extract.groupby(t).agg(
                    实验数=("临床试验编号", "count"),
                    企业数=("企业名称", "nunique"),
                    上市数=("上市情况", "sum"),
                    临床阶段=("所属分期", deal_stage)
                ).reset_index()
                detail_col = ["实验数", "企业数", "上市数", "临床阶段"]
                output_data = deal_data_log_percentile(group, detail_col, is_log_scale=True, expansion_factor=1.0)
                detail_result[data_key + "(" + t + f"{y}" + ")"] = output_data
                # 输出热力图数据
                long_data = thermodynamic_diagram(output_data, t, y, "成熟度得分（原始）")
                thermodynamic_diagram_result[data_key + "(" + t + f"{y}" + ")"] = long_data
                # 输出热力图数据（带年份）
                if t == "赛道":
                    with_year_cols = [t, "成熟度得分（原始）"]
                    with_year_data = output_data[with_year_cols].copy()
                    with_year_data["年份"] = y
                    thermodynamic_diagram_with_year[data_key + f"{y}"] = with_year_data

    # 合并数 把这几年的数据，全部合并到一起
    thermodynamic_diagram_with_year_all = {}
    for region_name in ["全国", "上海"]:
        region_data = []
        for data_key, data_value in thermodynamic_diagram_with_year.items():
            # 成熟度得分，扩100并保留两位小数
            data_value = data_value.copy()
            data_value["成熟度得分（原始）"] = data_value["成熟度得分（原始）"].apply(lambda x: f"{round(x * 100, 2)}")
            # 确保每个 data_value 是一个DataFrame
            if isinstance(data_value, pd.DataFrame) and data_key.__contains__(region_name):
                region_data.append(data_value)

        width_data = pd.concat(region_data, ignore_index=True)
        width_data_type_count = width_data["赛道"].value_counts()
        width_data = width_data[width_data["赛道"].isin(width_data_type_count[width_data_type_count > 4].index)]
        thermodynamic_diagram_with_year_all[region_name] = width_data
    excel_util.output_excel(output_dir, detail_result, f"case2_maturity_topsis_all.xlsx")
    excel_util.output_excel(output_dir, thermodynamic_diagram_result, f"case2_thermodynamic_diagram_all.xlsx")
    excel_util.output_excel(output_dir,
                            thermodynamic_diagram_with_year_all,
                            f"case2_thermodynamic_diagram_with_year_all.xlsx")
    print("案例二（赛道成熟度）处理完成！")


if __name__ == "__main__":
    main()
